package networkSimulation.simulation.animals.simulation

import networkSimulation.simulator.Simulator
import networkSimulation.visualisation.graph.graphviz.GraphvizVisualiser
import networkSimulation.simulation.animals.framework._
import util.Random
import networkSimulation.framework.{Edge, Graph}
import networkSimulation.statistic.StatisticsManager
import networkSimulation.statistic.basicStatistics.{EdgeNodeRatioPie, EdgeNodeRatioBar}
import networkSimulation.simulation.animals.statistic._

/**
 * This class represents a simulation.
 *
 * This simulation is a subType of Simulator, with types:
 *  - Animal as Node
 *  - Hunt as Edge
 *
 * This simulation has been extended with the GraphvizVisualizer trait, which means that this simulation
 * can take off-line snapshots of the state of the graph (the saveVisualGraph(graph) method)
 */
class Animals(graph: Graph[Animal,Hunt[Animal]]) extends Simulator[Animal,Hunt[Animal]](graph) with GraphvizVisualiser[Animal,Hunt[Animal]]{

  /**
   * Variable holding the path for the visual representations
   */
  private var outputPath = ""

  /**
   *  Provide statistics-support by adding a statisticsManager to this simulation.
   */
  private var statisticsManager = new StatisticsManager[Animal,Hunt[Animal]]()

  /**
   * Add statistics to the statisticsManager
   */
   statisticsManager.addStatistic(new AmountsAnimalsLine[Animal,Hunt[Animal]]())
   statisticsManager.addStatistic(new PercentageDeadBar[Animal,Hunt[Animal]]())
   statisticsManager.addStatistic(new AmountsAnimalBarEnd[Animal,Hunt[Animal]]())
   statisticsManager.addStatistic(new AmountsAnimalBarBegin[Animal,Hunt[Animal]]())
   statisticsManager.addStatistic(new WolfSurvivalPie[Animal,Hunt[Animal]]())
   statisticsManager.addStatistic(new RabbitSurvivalPie[Animal,Hunt[Animal]]())
   statisticsManager.addStatistic(new DinosaurSurvivalPie[Animal,Hunt[Animal]]())

  /**
   *  Start the simulation.
   *
   * @param turns
   *        The number of turns that the simulation will go through
   * @param path
   *        The path were all (temporary) files will be saved
   * @post The outputPath will be set
   *        | this.outputPath = path
   * @post A snapshot of the initial graph will be made
   *        | super.saveVisualGraph(outputPath)
   * @post The simulation will run 'turns' times
   *        | for 0 to turns do
   *        |   run()
   *        | end for
   */
  def startSimulation( turns : Int, path : String){
    this.outputPath = path
    clean(this.outputPath)
    super.saveVisualGraph(outputPath)
    for (i <- 0 until turns)
      run()

    statisticsManager.generateStatistics(outputPath);
  }

  /**
   * Run the simulation.
   *
   * @post  The simulation method of each node will be called
   *        | super.run()
   * @post  All dead animals will be removed from the graph
   *        | for each animal in graph
   *        |   if animal.isDead == true
   *        |     remove animal from graph
   *        |   end if
   *        | end for
   * @post  A snapshot of the new state of the graph will be made
   *        | super.saveVisualGraph(outputPath)
   * @post  Replace all edges in the graph by new ones
   *        | randomizeGraph(graph)
   */
  @Override
  override def run(){    
    super.run()
    for(animal <- this.graph.nodes){
      if (animal.isDead)
         this.graph.removeNode(animal)        
    }
    super.saveVisualGraph(this.outputPath)
    statisticsManager.updateStatistics(outputPath,graph)
    randomizeGraph(this.graph)
  }

  /**
   * Replace all edges in the graph.
   *
   * @param graph
   *        The graph in which all edges need to be replaced.
   * @post  All previous existing edges will be removed
   *        | graph.edges = List[...]()
   * @post  New edges will be randomly added
   *        All edges will be valid (only edges from Dinosaur->Wolf and Wolf->Rabbit will be created)
   */
  private def randomizeGraph(graph : Graph[Animal,Hunt[Animal]]){
    this.graph.edges = List[Hunt[Animal]]()
    var rabbits = List[Rabbit]()
    var wolfs = List[Wolf]()
    var dinosaurs = List[Dinosaur]()
    for (animal <- graph.nodes) {
      if (animal.isInstanceOf[Wolf]) wolfs ::= animal.asInstanceOf[Wolf]
      if (animal.isInstanceOf[Dinosaur]) dinosaurs ::= animal.asInstanceOf[Dinosaur]
      if (animal.isInstanceOf[Rabbit]) rabbits ::= animal.asInstanceOf[Rabbit]
    }
    for (i <- 0 until ((this.graph.nodes.length)-1)){
      if (dinosaurs.length > 0 && wolfs.length > 0 && rabbits.length > 0){
        val rnd1 = new Random().nextInt(dinosaurs.length)
        val rnd2 = new Random().nextInt(wolfs.length)
        val rnd3 = new Random().nextInt(wolfs.length)
        val rnd4 = new Random().nextInt(rabbits.length)
        if (new Random().nextInt(3)==2){
          graph.addEdge(new Hunt[Animal](dinosaurs.apply(rnd1),wolfs.apply(rnd2)))
        } else {
          graph.addEdge(new Hunt[Animal](wolfs.apply(rnd3),rabbits.apply(rnd4)))
        }
      } else if (dinosaurs.length == 0 && wolfs.length > 0 && rabbits.length > 0){
            val rnd3 = new Random().nextInt(wolfs.length)
            val rnd4 = new Random().nextInt(rabbits.length)
            graph.addEdge(new Hunt[Animal](wolfs.apply(rnd3),rabbits.apply(rnd4)))
      } else if (dinosaurs.length > 0 && wolfs.length > 0 && rabbits.length == 0){
          val rnd1 = new Random().nextInt(dinosaurs.length)
          val rnd2 = new Random().nextInt(wolfs.length)
          graph.addEdge(new Hunt[Animal](dinosaurs.apply(rnd1),wolfs.apply(rnd2)))
      }      
    }
  }

}